
local config = require 'config'
local alien = require 'alien'
local alienex = require 'alienex'
local bit = require 'bit'
local pairs, ipairs
    = pairs, ipairs
local max
    = math.max


module(...)


local lib = alien.load(config.sdl_path)
INIT_TIMER = 0x00000001
INIT_AUDIO = 0x00000010
INIT_VIDEO = 0x00000020
INIT_CDROM = 0x00000100
INIT_JOYSTICK = 0x00000200
INIT_NOPARACHUTE = 0x00100000
INIT_EVENTTHREAD = 0x01000000
INIT_EVERYTHING = 0x0000FFFF

local SDL_bool = 'int'
FALSE = 0
TRUE = 1

local function Func(name, types)
  local func = lib["SDL_" .. name]
  func:types(types)
  _M[name] = func
  return func
end

Func("Init", { ret="int"; "uint" --[[flags]] })
Func("InitSubSystem", { ret="int"; "uint" --[[flags]] })
Func("QuitSubSystem", { ret="void"; "uint" --[[flags]] })
Func("WasInit", { ret="uint"; "uint" --[[flags]] })
Func("Quit", { ret="void" })

-- rwops

local RWFromFile = Func("RWFromFile", { ret="pointer" --[[SDL_RWops*]]; "string" --[[file]], "string" --[[mode]] })

-- video subsystem
ALPHA_OPAQUE = 255
ALPHA_TRANSPARENT = 0

SWSURFACE	= 0x00000000
HWSURFACE	= 0x00000001
ASYNCBLIT	= 0x00000004
ANYFORMAT = 0x10000000
HWPALETTE = 0x20000000
DOUBLEBUF = 0x40000000
FULLSCREEN = 0x80000000
OPENGL = 0x00000002
OPENGLBLIT = 0x0000000A
RESIZABLE = 0x00000010
NOFRAME = 0x00000020
HWACCEL = 0x00000100
SRCCOLORKEY = 0x00001000
RLEACCELOK = 0x00002000
RLEACCEL = 0x00004000
SRCALPHA = 0x00010000
PREALLOC = 0x01000000

local SDL_GrabMode = 'int'
GRAB_QUERY = -1
GRAB_OFF = 0
GRAB_ON = 1
GRAB_FULLSCREEN = 2

Rect = alien.defstruct{
  {"x", "short"}; {"y", "short"};
  {"w", "ushort"}; {"h", "ushort"};
}

Color = alien.defstruct{
  {"r", "char"};
  {"g", "char"};
  {"b", "char"};
  {"unused", "char"};
}
Colour = Color

Palette = alien.defstruct{
  {"ncolors", "int"};
  {"colors", "pointer" --[[SDL_Color*]]};
}

PixelFormat = alien.defstruct{
  {"palette", "pointer" --[[SDL_Palette*]]};
  {"BitsPerPixel", "char"};
  {"BytesPerPixel", "char"};
  {"Rloss", "char"};
  {"Gloss", "char"};
  {"Bloss", "char"};
  {"Aloss", "char"};
  {"Rshift", "char"};
  {"Gshift", "char"};
  {"Bshift", "char"};
  {"Ashift", "char"};
  {"Rmask", "uint"};
  {"Gmask", "uint"};
  {"Bmask", "uint"};
  {"Amask", "uint"};
  {"colorkey", "uint"};
  {"alpha", "char"};
}

Surface = alien.defstruct{
  {"flags", "uint"};
  {"format", "pointer" --[[SDL_PixelFormat*]]};
  {"w", "int"}; {"h", "int"};
  {"pitch", "ushort"};
  {"pixels", "pointer" --[[void*]]};
  {"offset", "int"};
  {"hwdata", "pointer" --[[struct private_hwdata*]]};
  {"clip_rect_x", "short"};
  {"clip_rect_y", "short"};
  {"clip_rect_w", "ushort"};
  {"clip_rect_h", "ushort"};
  {"unused1", "uint"};
  {"locked", "uint"};
  {"map", "pointer" --[[struct SDL_BlitMap*]]};
  {"format_version", "uint"};
  {"refcount", "int"};
}

function MUSTLOCK(surface)
  return (surface.offset ~= 0) or (bit.band(surface.flags, bit.bor(HWSURFACE, ASYNCBLIT, RLEACCEL)) ~= 0)
end

blit_types = { ret="int";
  "pointer" --[[SDL_Surface* src]], "pointer" --[[SDL_Rect* srcrect]], "pointer" --[[SDL_Surface* dst]], "pointer" --[[SDL_Rect* dstrect]] }

VideoInfo = alien.defstruct{
  {"hw_available", "uint"};
  {"wm_available", "uint"};
  {"UnusedBits1", "uint"};
  {"UnusedBits2", "uint"};
  {"blit_hw", "uint"};
  {"blit_hw_CC", "uint"};
  {"blit_hw_A", "uint"};
  {"blit_sw", "uint"};
  {"blit_sw_CC", "uint"};
  {"blit_sw_A", "uint"};
  {"blit_fill", "uint"};
  {"UnusedBits3", "uint"};
  {"video_mem", "uint"};
  {"vfmt", "pointer" --[[SDL_PixelFormat*]]};
  {"current_w", "int"};
  {"current_h", "int"};
}

YV12_OVERLAY = 0x32315659
IYUV_OVERLAY = 0x56555949
YUY2_OVERLAY = 0x32595559
UYVY_OVERLAY = 0x59565955
YVYU_OVERLAY = 0x55595659

Overlay = alien.defstruct{
  {"format", "uint"};
  {"w", "int"}; {"h", "int"};
  {"planes", "int"};
  {"pitches", "pointer" --[[Uint16*]]};
  {"pixels", "pointer" --[[Uint8**]]};
  {"hwfunc", "pointer" --[[struct private_yuvhwfuncs*]]};
  {"hwdata", "pointer" --[[struct private_yuvhwdata*]]};
  {"hw_overlay", "uint"};
  {"UnusedBits", "uint"};
}

GL_RED_SIZE = 0
GL_GREEN_SIZE = 1
GL_BLUE_SIZE = 2
GL_ALPHA_SIZE = 3
GL_BUFFER_SIZE = 4
GL_DOUBLEBUFFER = 5
GL_DEPTH_SIZE = 6
GL_STENCIL_SIZE = 7
GL_ACCUM_RED_SIZE = 8
GL_ACCUM_GREEN_SIZE = 9
GL_ACCUM_BLUE_SIZE = 10
GL_ACCUM_ALPHA_SIZE = 11
GL_STEREO = 12
GL_MULTISAMPLEBUFFERS = 13
GL_MULTISAMPLESAMPLES = 14
GL_ACCELERATED_VISUAL = 15
GL_SWAP_CONTROL = 16

LOGPAL = 0x01
PHYSPAL = 0x02

Func("VideoInit", { ret="int", "string" --[[driver_name]], "uint" --[[flags]] })
Func("VideoQuit", { ret="void" })
Func("VideoDriverName", { ret="pointer" --[[char*]]; "pointer" --[[char* namebuf]], "int" --[[maxlen]] })
Func("GetVideoSurface", { ret="pointer" --[[SDL_Surface*]] })
Func("GetVideoInfo", { ret="pointer" --[[const SDL_VideoInfo*]] })
Func("VideoModeOK", { ret="int"; "int" --[[width]], "int" --[[height]], "int" --[[bpp]], "uint" --[[flags]] })
Func("ListModes", { ret="pointer" --[[SDL_Rect**]]; "pointer" --[[SDL_PixelFormat*]], "uint" --[[flags]] })
Func("SetVideoMode", { ret="pointer" --[[SDL_Surface*]]; "int" --[[width]], "int" --[[height]], "int" --[[bpp]], "uint" --[[flags]] })
Func("UpdateRects", { ret="void"; "pointer" --[[SDL_Surface* screen]], "int" --[[numrects]], "pointer" --[[SDL_Rect* rects]] })
Func("UpdateRect", { ret="void"; "pointer" --[[SDL_Surface* screen]], "short" --[[x]], "short" --[[y]], "uint" --[[w]], "uint" --[[h]] })
Func("Flip", { ret="int"; "pointer" --[[SDL_Surface*]] })
Func("SetGamma", { ret="int"; "float" --[[red]], "float" --[[green]], "float" --[[blue]] })
Func("SetGammaRamp", { ret="int"; "pointer" --[[const Uint16* red]], "pointer" --[[const Uint16* green]], "pointer" --[[const Uint16* blue]] })
Func("GetGammaRamp", { ret="int"; "pointer" --[[Uint16* red]], "pointer" --[[Uint16* green]], "pointer" --[[Uint16* blue]] })
Func("SetColors", { ret="int"; "pointer" --[[SDL_Surface*]], "pointer" --[[SDL_Color* colors]], "int" --[[firstcolor]], "int" --[[ncolors]] })
Func("SetPalette", { ret="int"; "pointer" --[[SDL_Surface*]], "int" --[[flags]], "pointer" --[[SDL_Color*]], "int" --[[firstcolor]], "int" --[[ncolors]] })
Func("MapRGB", { ret="uint"; "pointer" --[[const SDL_PixelFormat*]], "char" --[[r]], "char" --[[g]], "char" --[[b]] })
Func("MapRGBA", { ret="uint"; "pointer" --[[const SDL_PixelFormat*]], "char" --[[r]], "char" --[[g]], "char" --[[b]], "char" --[[a]] })
Func("GetRGB", { ret="void";
  "uint" --[[pixel]], "pointer" --[[SDL_PixelFormat*]], "ref char" --[[r]], "ref char" --[[g]], "ref char" --[[b]] })
Func("GetRGBA", { ret="void";
  "uint" --[[pixel]], "pointer" --[[SDL_PixelFormat*]], "ref char" --[[r]], "ref char" --[[g]], "ref char" --[[b]], "ref char" --[[a]] })
Func("CreateRGBSurface", { ret="pointer" --[[SDL_Surface*]];
  "uint" --[[flags]], "int" --[[width]], "int" --[[height]], "int" --[[depth]],
  "uint" --[[Rmask]], "uint" --[[Gmask]], "uint" --[[Bmask]], "uint" --[[Amask]] })
AllocSurface = CreateRGBSurface
Func("CreateRGBSurfaceFrom", { ret="pointer" --[[SDL_Surface*]];
  "pointer" --[[void* pixels]], "int" --[[width]], "int" --[[height]], "int" --[[depth]], "int" --[[pitch]],
  "uint" --[[Rmask]], "uint" --[[Bmask]], "uint" --[[Amask]] })
Func("FreeSurface", { ret="void"; "pointer" --[[SDL_Surface*]] })
Func("LockSurface", { ret="int"; "pointer" --[[SDL_Surface*]] })
Func("UnlockSurface", { ret="void"; "pointer" --[[SDL_Surface*]] })
local LoadBMP_RW = Func("LoadBMP_RW", { ret="pointer" --[[SDL_Surface*]]; "pointer" --[[SDL_RWops* src]], "int" --[[freesrc]] })
function LoadBMP(file)
  return LoadBMP_RW(RWFromFile(file, "rb"), 1)
end
local SaveBMP_RW = Func("SaveBMP_RW", { ret="int"; "pointer" --[[SDL_Surface*]], "pointer" --[[SDL_RWops* dst]], "int" --[[freedst]] })
function SaveBMP(surface, file)
  return SaveBMP_RW(surface, RWFromFile(file, "wb"), 1)
end
Func("SetColorKey", { ret="int"; "pointer" --[[SDL_Surface*]], "uint" --[[flag]], "uint" --[[key]] })
Func("SetAlpha", { ret=SDL_bool; "uint" --[[flag]], "char" --[[alpha]] })
Func("SetClipRect", { ret=SDL_bool; "pointer" --[[SDL_Surface*]], "pointer" --[[const SDL_Rect*]] })
Func("GetClipRect", { ret="void"; "pointer" --[[SDL_Surface*]], "pointer" --[[SDL_Rect *rect]] })
Func("ConvertSurface", { ret="pointer" --[[SDL_Surface*]]; "pointer" --[[SDL_Surface*]], "pointer" --[[SDL_PixelFormat*]], "uint" --[[flags]] })
Func("UpperBlit", { ret="int";
  "pointer" --[[SDL_Surface* src]], "pointer" --[[SDL_Rect* srcrect]],
  "pointer" --[[SDL_Surface* dst]], "pointer" --[[SDL_Rect* dstrect]] })
SDL_BlitSurface = SDL_UpperBlit
Func("LowerBlit", { ret="int";
  "pointer" --[[SDL_Surface* src]], "pointer" --[[SDL_Rect* srcrect]],
  "pointer" --[[SDL_Surface* dst]], "pointer" --[[SDL_Rect* dstrect]] })
Func("FillRect", { ret="int"; "pointer" --[[SDL_Surface* dst]], "pointer" --[[SDL_Rect*]], "uint" --[[color]] })
Func("DisplayFormat", { ret="pointer" --[[SDL_Surface*]]; "pointer" --[[SDL_Surface*]] })
Func("DisplayFormatAlpha", { ret="pointer" --[[SDL_Surface*]]; "pointer" --[[SDL_Surface*]] })

--[=[
extern DECLSPEC SDL_Overlay * SDLCALL SDL_CreateYUVOverlay("int" --[[width]], "int" --[[height]],
				Uint32 format, SDL_Surface *display })
extern DECLSPEC int SDLCALL SDL_LockYUVOverlay(SDL_Overlay *overlay })
extern DECLSPEC void SDLCALL SDL_UnlockYUVOverlay(SDL_Overlay *overlay })
extern DECLSPEC int SDLCALL SDL_DisplayYUVOverlay(SDL_Overlay *overlay, SDL_Rect *dstrect })
extern DECLSPEC void SDLCALL SDL_FreeYUVOverlay(SDL_Overlay *overlay })
--]=]
Func("GL_LoadLibrary", { ret="int"; "string" --[[path]] })
Func("GL_GetProcAddress", { ret="pointer"; "string" --[[proc]] })
Func("GL_SetAttribute", { ret="int"; "int" --[[SDL_GLattr]], "int" --[[value]] })
Func("GL_GetAttribute", { ret="int"; "int" --[[SDL_GLattr]], "ref int" --[[value]] })
Func("GL_SwapBuffers", { ret="void" })
Func("GL_UpdateRects", { ret="void"; "int" --[[numrects]], "pointer" --[[SDL_Rect*]] })
Func("GL_Lock", { ret="void" })
Func("GL_Unlock", { ret="void" })
Func("WM_SetCaption", { ret="void"; "string" --[[title]], "string" --[[icon]] })
Func("WM_GetCaption", { ret="void"; "pointer" --[[char** title]], "pointer" --[[char** icon]] })
Func("WM_SetIcon", { ret="void"; "pointer" --[[SDL_Surface*]], "pointer" --[[Uint8* mask]] })
Func("WM_IconifyWindow", { ret="int" })
Func("WM_ToggleFullScreen", { ret="int"; "pointer" --[[SDL_Surface*]] })
Func("WM_GrabInput", { ret=SDL_GrabMode; SDL_GrabMode })

local function InitEvents(def)
  local maxsize = 0
  for k,v in pairs(def) do
    v = alienex.defstruct(v)
    _M[k] = v
    def[k] = v
    maxsize = max(maxsize, v.size)
  end
  for k,v in pairs(def) do
    v.size = maxsize
  end
end

InitEvents({
  Event = {
    {"type", "char"};
  };
  ActiveEvent = {
    {"type", "char"};
    {"gain", "char"};
    {"state", "char"};
  };
  KeyboardEvent = {
    {"type", "char"};
    {"state", "char"};
    {"keysym_scancode", "char"};
    {"keysym_sym", "int" --[[SDL_key]]};
    {"keysym_mod", "int" --[[SDLMod]]};
    {"unicode", "ushort"};
  };
  MouseMotionEvent = {
    {"type", "char"};
    {"which", "char"};
    {"state", "char"};
    {"x", "ushort"};
    {"y", "ushort"};
    {"xrel", "short"};
    {"yrel", "short"};
  };
  MouseButtonEvent = {
    {"type", "char"};
    {"which", "char"};
    {"button", "char"};
    {"state", "char"};
    {"x", "ushort"};
    {"y", "ushort"};
  };
  JoyAxisEvent = {
    {"type", "char"};
    {"which", "char"};
    {"axis", "char"};
    {"value", "short"};
  };
  JoyButtonEvent = {
    {"type", "char"};
    {"which", "char"};
    {"button", "char"};
    {"state", "char"};
  };
  JoyHatEvent = {
    {"type", "char"};
    {"which", "char"};
    {"hat", "char"};
    {"value", "char"};
  };
  JoyBallEvent = {
    {"type", "char"};
    {"which", "char"};
    {"ball", "char"};
    {"xrel", "short"};
    {"yrel", "short"};
  };
  ResizeEvent = {
    {"type", "char"};
    {"w", "int"};
    {"h", "int"};
  };
  ExposeEvent = {
    {"type", "char"};
  };
  SysWMEvent = {
    {"type", "char"};
  };
  UserEvent = {
    {"type", "char"};
    {"code", "int"};
    {"data1", "pointer"};
    {"data2", "pointer"};
  };
  QuitEvent = {
    {"type", "char"};
  };
})

ACTIVEEVENT = 1
KEYDOWN = 2
KEYUP = 3
MOUSEMOTION = 4
MOUSEBUTTONDOWN = 5
MOUSEBUTTONUP = 6
JOYAXISMOTION = 7
JOYBALLMOTION = 8
JOYHATMOTION = 9
JOYBUTTONDOWN = 10
JOYBUTTONUP = 11
QUIT = 12
SYSWMEVENT = 13
EVENT_RESERVEDA = 14
EVENT_RESERVEDB = 15
VIDEORESIZE = 16
VIDEOEXPOSE = 17
USEREVENT = 24

NUMEVENTS = 32

local function EVENTMASK(X)  return bit.lshift(1,X);  end
ACTIVEEVENTMASK = EVENTMASK(ACTIVEEVENT)
KEYDOWNMASK = EVENTMASK(KEYDOWN)
KEYUPMASK = EVENTMASK(KEYUP)
KEYEVENTMASK = EVENTMASK(KEYDOWN) +
                EVENTMASK(KEYUP)
MOUSEMOTIONMASK = EVENTMASK(MOUSEMOTION)
MOUSEBUTTONDOWNMASK = EVENTMASK(MOUSEBUTTONDOWN)
MOUSEBUTTONUPMASK = EVENTMASK(MOUSEBUTTONUP)
MOUSEEVENTMASK = EVENTMASK(MOUSEMOTION) +
                  EVENTMASK(MOUSEBUTTONDOWN) +
                  EVENTMASK(MOUSEBUTTONUP)
JOYAXISMOTIONMASK = EVENTMASK(JOYAXISMOTION)
JOYBALLMOTIONMASK = EVENTMASK(JOYBALLMOTION)
JOYHATMOTIONMASK = EVENTMASK(JOYHATMOTION)
JOYBUTTONDOWNMASK = EVENTMASK(JOYBUTTONDOWN)
JOYBUTTONUPMASK = EVENTMASK(JOYBUTTONUP)
JOYEVENTMASK = EVENTMASK(JOYAXISMOTION) +
                EVENTMASK(JOYBALLMOTION) +
                EVENTMASK(JOYHATMOTION) +
                EVENTMASK(JOYBUTTONDOWN) +
                EVENTMASK(JOYBUTTONUP)
VIDEORESIZEMASK = EVENTMASK(VIDEORESIZE)
VIDEOEXPOSEMASK = EVENTMASK(VIDEOEXPOSE)
QUITMASK = EVENTMASK(QUIT)
SYSWMEVENTMASK = EVENTMASK(SYSWMEVENT)

ALLEVENTS = 0xFFFFFFFF

Func("PumpEvents", { ret="void" })
ADDEVENT = 0
PEEKEVENT = 1
GETEVENT = 2
Func("PeepEvents", { ret="int"; "pointer" --[[SDL_Event*]], "int" --[[numevents]], "int" --[[SDL_eventaction]], "uint" --[[mask]] })
Func("PollEvent", { ret="int"; "pointer" --[[SDL_Event*]] })
Func("WaitEvent", { ret="int"; "pointer" --[[SDL_Event*]] })
Func("PushEvent", { ret="int"; "pointer" --[[SDL_Event*]] })
EventFilter_types = { ret="int"; "pointer" --[[SDL_Event*]] }
Func("SetEventFilter", { ret="void"; "callback" --[[SDL_EventFilter]] })
Func("GetEventFilter", { ret="callback" --[[SDL_EventFilter]] })
QUERY	= -1
IGNORE = 0
DISABLE = 0
ENABLE = 1
Func("EventState", { ret="char"; "char" --[[type]], "int" --[[state]] })

Func("GetTicks", { ret="uint" })
Func("Delay", { ret="void"; "uint" })
TimerCallback_types = { ret="uint"; "uint" --[[interval]] }
Func("SetTimer", { ret="int"; "uint" --[[interval]], "callback" --[[SDL_TimerCallback]] })
NewTimerCallback_types = { ret="uint"; "uint" --[[interval]], "pointer" --[[param]] }
Func("AddTimer", { ret="pointer" --[[SDL_TimerID]]; "uint" --[[interval]], "callback" --[[SDL_NewTimerCallback]], "pointer" --[[param]] })
Func("RemoveTimer", { ret=SDL_bool; "pointer" --[[SDL_TimerID]] })

APPMOUSEFOCUS	= 0x01
APPINPUTFOCUS	= 0x02
APPACTIVE = 0x04

PRESSED = 1
RELEASED = 0

function BUTTON(X)  return bit.lshift(1, X-1);  end
BUTTON_LEFT = 1
BUTTON_MIDDLE = 2
BUTTON_RIGHT = 3
BUTTON_WHEELUP = 4
BUTTON_WHEELDOWN = 5
BUTTON_X1 = 6
BUTTON_X2 = 7
BUTTON_LMASK = BUTTON(BUTTON_LEFT)
BUTTON_MMASK = BUTTON(BUTTON_MIDDLE)
BUTTON_RMASK = BUTTON(BUTTON_RIGHT)
BUTTON_X1MASK = BUTTON(BUTTON_X1)
BUTTON_X2MASK = BUTTON(BUTTON_X2)

Func("GetWMInfo", { ret="int"; "pointer" --[[SDL_SysWMinfo*]] })

if (alien.platform == 'windows') then

  SysWMinfo = alien.defstruct{
    {"version_major", "char"};
    {"version_minor", "char"};
    {"version_patch", "char"};
    {"window", "pointer" --[[HWND]]};
    {"hglrc", "pointer" --[[HGLRC]]};
  }

end
